package javaer.starter.model.context;

import com.intellij.openapi.module.Module;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import javaer.starter.idea.util.MavenApacheUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Exclusion;
import org.apache.maven.project.MavenProject;
import org.jetbrains.idea.maven.model.MavenArtifactNode;

import java.io.Serializable;
import java.util.*;

/**
 * Idea拿到的依赖没有排除项
 * Maven拿到的依赖不能关联到version，因为多个module的MavenProject互不认识，无法拿到root配置的依赖版本
 *
 * @author yangzhen
 * @date 2023/7/5 10:48
 * @since 1.0.0
 */
public class MavenContext implements Serializable {

    // 根项目
    public MavenProject rootMavenProject;
    // 根项目的artifactId
    public String rootArtifactId;
    // 所有模块的artifactId
    public Set<String> artifactIds = new HashSet<>();
    // 模块artifactId=properties标签
    public Map<String, Properties> artifactId_properties = new HashMap<>();
    // 模块artifactId=description标签
    public Map<String, String> artifactId_description = new HashMap<>();
    // 模块artifactId=pom路径
    public Map<String, String> artifactId_pomPath = new HashMap<>();
    // 模块artifactId=关联的依赖
    public Map<String, Set<String>> artifactId_moduleDependency = new HashMap<>();
    // 模块artifactId=关联的依赖
    public Map<String, List<Dependency>> artifactId_dependency = new HashMap<>();
    // 模块artifactId=（依赖artifactId=MavenArtifactNode），为了拿到version
    public Map<String, Map<String, MavenArtifactNode>> artifactId_artifactId_mavenArtifactNode = new HashMap<>();
    // 模块artifactId=（依赖artifactId=dependencyManagement管理的依赖），为了拿到排除的依赖
    public Map<String, Map<String, Dependency>> artifactId_artifactId_managementDependency = new HashMap<>();
    // 模块artifactId=java文件
    public Map<String, List<PsiJavaFile>> artifactId_psiJavaFile = new HashMap<>();
    // 模块artifactId=所有文件
    public Map<String, List<PsiFile>> artifactId_psiFile = new HashMap<>();
    // 类的权限定名=对应文件
    public Map<String, PsiJavaFile> qualifiedName_psiJavaFile = new HashMap<>();
    // 类的权限定名
    public Set<String> qualifiedNames = new HashSet<>();
    // 要模板化的文件总数，用于进度条
    public int fileCount = 0;
    // 模块artifactId=模块
    public Map<String, Module> artifactId_module = new HashMap<>();
    // 依赖artifactId
    public Set<String> dependencyIdSet = new HashSet<>();

    public void addModule(String artifactId, Module module) {
        artifactId_module.put(artifactId, module);
    }

    public MavenProject addMavenProject(String pomPath) {
        MavenProject mavenProject = MavenApacheUtils.getMavenProject(pomPath);
        String artifactId = mavenProject.getArtifactId();
        artifactIds.add(artifactId);
        artifactId_pomPath.put(artifactId, pomPath);
        String description = mavenProject.getDescription();
        artifactId_description.put(artifactId, description);
        addDependency(artifactId, mavenProject.getDependencies());
        artifactId_dependency.put(artifactId, mavenProject.getDependencies());
        artifactId_properties.put(artifactId, mavenProject.getProperties());
        DependencyManagement dependencyManagement = mavenProject.getDependencyManagement();
        dependencyManagement = dependencyManagement == null ? new DependencyManagement() : dependencyManagement;
        List<Dependency> dependencies = dependencyManagement.getDependencies();
        Map<String, Dependency> managementDependency = artifactId_artifactId_managementDependency.getOrDefault(artifactId, new HashMap<>());
        if (dependencies != null) {
            for (Dependency dependency : dependencies) {
                managementDependency.put(dependency.getArtifactId(), dependency);
            }
        }
        artifactId_artifactId_managementDependency.put(artifactId, managementDependency);
        return mavenProject;
    }

    public void addDependency(String artifactId, List<Dependency> dependencies) {
        artifactId_dependency.put(artifactId, dependencies);
        if (CollectionUtils.isNotEmpty(dependencies)) {
            for (Dependency dependency : dependencies) {
                dependencyIdSet.add(dependency.getArtifactId());
            }
        }
    }

    public void addModuleDep(String artifactId, String moduleDependency) {
        Set<String> moduleDependencies = artifactId_moduleDependency.getOrDefault(artifactId, new HashSet<>());
        moduleDependencies.add(moduleDependency);
        artifactId_moduleDependency.put(artifactId, moduleDependencies);
    }

    public void addMavenProjectRoot(String rootPath) {
        this.rootMavenProject = addMavenProject(rootPath);
        this.rootArtifactId = this.rootMavenProject.getArtifactId();
    }

    public void addMavenProject(Set<String> modulePaths) {
        modulePaths.forEach((v) -> {
            addMavenProject(v);
        });
    }

    public void addPsiJavaFile(Map<String, List<PsiJavaFile>> psiJavaFileMap) {
        artifactId_psiJavaFile.putAll(psiJavaFileMap);
        psiJavaFileMap.values().forEach((list) -> {
            for (PsiJavaFile psiJavaFile : list) {
                String packageName = psiJavaFile.getPackageName();
                String name = psiJavaFile.getName();
                String qualifiedName = packageName + "." + name.substring(0, name.length() - 5);
                qualifiedName_psiJavaFile.put(qualifiedName, psiJavaFile);
                this.qualifiedNames.add(qualifiedName);
            }
        });
    }

    public void addPsiFile(Map<String, List<PsiFile>> psiFileMap) {
        artifactId_psiFile.putAll(psiFileMap);
        fileCount = fileCount + (int) psiFileMap.values().stream().flatMap(List::stream).count();
    }

    /**
     * 添加模块对应的依赖，这里拿的是idea的，可以获取到依赖的版本号
     *
     * @param artifactId
     * @param mavenArtifactNodes
     */
    public void addMavenArtifactNodes(String artifactId, List<MavenArtifactNode> mavenArtifactNodes) {
        for (MavenArtifactNode mavenArtifactNode : mavenArtifactNodes) {
            String depArtifactId = mavenArtifactNode.getArtifact().getArtifactId();
            Map<String, MavenArtifactNode> stringMavenArtifactNodeMap = artifactId_artifactId_mavenArtifactNode.getOrDefault(artifactId, new HashMap<>());
            stringMavenArtifactNodeMap.put(depArtifactId, mavenArtifactNode);
            artifactId_artifactId_mavenArtifactNode.put(artifactId, stringMavenArtifactNodeMap);
        }
    }

    /**
     * 数据清洗
     */
    public void clean() {
        // 遍历所有的artifactId_dependency，将所有的artifactIds的依赖都去掉
        artifactId_dependency.entrySet()
                .stream()
                .forEach(entry -> entry.getValue()
                        .removeIf(dependency -> artifactIds.contains(dependency.getArtifactId())));
        // 遍历所有的artifactId_dependency，填充所有的依赖版本
        Set<Map.Entry<String, List<Dependency>>> entries = artifactId_dependency.entrySet();
        Map<String, Dependency> root_managementDependency = artifactId_artifactId_managementDependency.get(rootArtifactId);
        for (Map.Entry<String, List<Dependency>> entry : entries) {
            String key = entry.getKey();
            Map<String, MavenArtifactNode> stringMavenArtifactNodeMap = artifactId_artifactId_mavenArtifactNode.getOrDefault(key, new HashMap<>());
            Map<String, Dependency> managementDependency = artifactId_artifactId_managementDependency.get(key);
            List<Dependency> value = entry.getValue();
            for (Dependency dependency : value) {
                MavenArtifactNode mavenArtifactNode = stringMavenArtifactNodeMap.get(dependency.getArtifactId());
                if (mavenArtifactNode != null) {
                    String version = mavenArtifactNode.getArtifact().getVersion();
                    if (StringUtils.isNotBlank(version)) {
                        dependency.setVersion(version);
                    }
                }
                dealExclusion(dependency, managementDependency);
                dealExclusion(dependency, root_managementDependency);
            }
        }
        System.out.println();
    }

    // 将管理的依赖的排除依赖添加到当前依赖中
    private void dealExclusion(Dependency dependency, Map<String, Dependency> managementDependency) {
        Dependency md = managementDependency.get(dependency.getArtifactId());
        if (md != null) {
            List<Exclusion> exclusions = md.getExclusions();
            if (exclusions != null) {
                for (Exclusion exclusion : exclusions) {
                    dependency.addExclusion(exclusion);
                }
            }
        }
    }
}
